/********************************************************************/
/*          Sio32MultiLoad.c                                        */
/*            32bit Serial Communication Multi-load Library         */
/*            (Send Timer Interrupt)                                */
/*                                                                  */
/*          Copyright (C) 2001 NINTENDO Co.,Ltd.                    */
/********************************************************************/
//#define CODE32
#include "Sio32MultiLoad.h"


/*-------------------- Global Variables ----------------------------*/

Sio32MultiLoadArea  S32ml;    // 32bit serial communication multi-load work area
static const u8 MultiSioLib_Var[]="Sio32MultiLoad010214";


/*------------------------------------------------------------------*/
/*       32bit Serial communication multi-load initialization       */
/*------------------------------------------------------------------*/

void Sio32MultiLoadInit(u32 Type, void *Datap)
{
    s32     CheckSum = 0;
    int     i;

    *(vu16 *)REG_IME = 0;
    *(vu16 *)REG_IE &= ~(SIO_INTR_FLAG                  // Disables SIO and timer interrupt
                       | SIO32ML_TIMER_INTR_FLAG);
    *(vu16 *)REG_IME = 1;

    CpuClear(0, &S32ml, sizeof(S32ml), 32);             // Clears 32bit serial communication work area

#ifdef SIO32ML_DI_FUNC_FAST                             // Copies function
    CpuCopy(Sio32MultiLoadIntr, IntrFuncBuf, sizeof(IntrFuncBuf), 32);
#endif

    *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE                // Stops multi-play communication
                        | MULTI_SIO_BAUD_RATE_NO;

    S32ml.Datap = Datap;                                // Sets data pointer
    S32ml.DataCounter = -1;

    if (Type) {                                         // Sets Master
        *(vu32 *)REG_SIO32ML_TIMER = 0;                     // Resets timer

        S32ml.Type = SIO_SCK_IN;

        for (i=0; i<SIO32ML_BLOCK_SIZE/4; i++)              // Calculates sending data check sum
            CheckSum += ((s32 *)Datap)[i];
        S32ml.CheckSum = ~CheckSum;

        *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;               // Switches to SIO mode (preceding code)
        *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE
                            | SIO_SCK_IN | SIO_IN_SCK_256K;
    }
}


/*------------------------------------------------------------------*/
/*        32bit serial communication multi-load main                */
/*------------------------------------------------------------------*/

u32 Sio32MultiLoadMain(u32 *ProgressCounterp)
{
    s32     DataCounter, DataCounterBak;
    int     i, ii;

    switch (S32ml.State) {
        case 0: if (S32ml.Type || S32ml.FrameCounter)       // Slave SIO Mode switch delay wait
                    S32ml.State = 1;
                break;
        case 1: if (S32ml.Type == SIO_SCK_IN) {             // Master wait
                    if (S32ml.FrameCounter < SIO32ML_MODE_WAIT_FRAMES)
                        break;
                } else                                      // Slave
                    *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;   // Switches to SIO mode

                *(vu32 *)REG_SIODATA32 = 0;                 // Clears data registery
                *(vu16 *)REG_IF = SIO_INTR_FLAG             // Resets IF
                                | SIO32ML_TIMER_INTR_FLAG;

                if (S32ml.Type == SIO_SCK_IN){              // Sets Master
                    *(vu16 *)REG_SIOCNT |= SIO_ENABLE;          // Starts communication

                    *(vu32 *)REG_SIO32ML_TIMER                  // Starts timer
                                     = SIO32ML_TIMER_COUNT
                                     | TMR_PRESCALER_1CK
                                     | TMR_IF_ENABLE | TMR_ENABLE;

                    *(vu16 *)REG_IME = 0;
                    *(vu16 *)REG_IE |= SIO32ML_TIMER_INTR_FLAG; // Enables timer interrupt
                    *(vu16 *)REG_IME = 1;
                } else {                                    // Sets Slave
                    *(vu16 *)REG_SIOCNT |= SIO_IF_ENABLE        // Starts communication
                                         | SIO_ENABLE;

                    *(vu16 *)REG_IME = 0;
                    *(vu16 *)REG_IE |= SIO_INTR_FLAG;           // Enables SIO interrupt
                    *(vu16 *)REG_IME = 1;
                }

                S32ml.FrameCounter = 0;
                S32ml.State = 2;
                break;
        case 2: DataCounter = S32ml.DataCounter;
                DataCounterBak = DataCounter;
                if (DataCounter > SIO32ML_BLOCK_SIZE/4)
                                            DataCounter = SIO32ML_BLOCK_SIZE/4;
                else if (DataCounter < 0)   DataCounter = 0;
                if (ProgressCounterp)
                   *ProgressCounterp = DataCounter;         // Set progress counter

                if (S32ml.Type != SIO_SCK_IN) {             // Slave
                    while (S32ml.CheckSumCounter < DataCounter) // Receiving data sum check
                        S32ml.CheckSumTmp += ((s32 *)S32ml.Datap)[S32ml.CheckSumCounter++];
                    if (DataCounterBak > SIO32ML_BLOCK_SIZE/4)
                        if ((S32ml.CheckSum += S32ml.CheckSumTmp) == -1)
                            S32ml.DownloadSuccessFlag = 1;      // Succeeds download
                }
                if (DataCounterBak > SIO32ML_BLOCK_SIZE/4   // Complete communication
                 || S32ml.FrameCounter == SIO32ML_LD_TIMEOUT_FRAMES)
                    S32ml.State = 3;                        // Load time out
                break;
        case 3: *(vu16 *)REG_IME = 0;
                *(vu16 *)REG_IE &= ~(SIO_INTR_FLAG          // Disables SIO and timer interrupt
                                   | SIO32ML_TIMER_INTR_FLAG);
                *(vu16 *)REG_IME = 1;

                *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;       // Stops SIO32
                *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE;       // Switches to SIO mode
                *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE
                                    | MULTI_SIO_BAUD_RATE_NO;
                *(vu64 *)REG_SIODATA32 = 0;                 // Clears data register

                if (S32ml.Type)                             // Master
                    *(vu32 *)REG_SIO32ML_TIMER = 0;             // Resets timer

                *(vu16 *)REG_IF  =  SIO_INTR_FLAG           // Resets IF
                                 |  SIO32ML_TIMER_INTR_FLAG;

                if (!S32ml.Type)  return 1;                 // Slave comes back first

                S32ml.FrameCounter = 0;
                S32ml.State = 4;
                break;
        case 4: if (S32ml.FrameCounter >= 3)                // Master comes back later
                    return 1;
    }

    S32ml.FrameCounter++;

    return  0;
}


/*==================================================================*/
/*     32bit Serial Communication Multi-load Interrupt Routine      */
/*==================================================================*/

void Sio32MultiLoadIntr(void)
{
    u32     RecvTmp;
    u32    *BufpTmp;


    // Saves received data

    RecvTmp = *(u32 *)REG_SIODATA32;

    // Slave prepares to receive
    if (S32ml.Type != SIO_SCK_IN)
        *(vu16 *)REG_SIOCNT |= SIO_ENABLE;


    if (S32ml.Type == SIO_SCK_IN) {
        *(vu16 *)REG_SIO32ML_TIMER_H = 0;                   // Stopes timer

        // Process sending data

        if (S32ml.DataCounter < 0)                          // Sets synchronous data
                *(vu32 *)REG_SIODATA32 = SIO32ML_SYNC_DATA;
        else if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4)  // Sets sending data
                *(vu32 *)REG_SIODATA32 = S32ml.Datap[S32ml.DataCounter];
        else    *(vu32 *)REG_SIODATA32 = S32ml.CheckSum;    // Sets check sum
    } else {

        // Process receiving data

        if (S32ml.DataCounter < 0) {                        // Checks synchronous data
            if (RecvTmp != SIO32ML_SYNC_DATA)
                S32ml.DataCounter--;
        } else if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4)// Saves receiving data
                S32ml.Datap[S32ml.DataCounter] = RecvTmp;
        else    S32ml.CheckSum = RecvTmp;                   // Saves check sum 
    }


    if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4 + 3) {
        S32ml.DataCounter++;

        // Master starts to sending

        if (S32ml.Type == SIO_SCK_IN) {
                *(vu16 *)REG_SIOCNT |= SIO_ENABLE;          // Restarts sending
                *(vu16 *)REG_SIO32ML_TIMER_H                // Restarts timer
                                 = (TMR_PRESCALER_1CK
                                  | TMR_IF_ENABLE | TMR_ENABLE) >> 16;
        }
    }
}

